import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:im_demo/utils/logger_util.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

import '../constant/storage_key.dart';

class WebSocketServiceProtobuf extends GetxService {
  static WebSocketServiceProtobuf get to => Get.find();

  final RxBool isConnected = false.obs;
  final RxInt retryCount = 0.obs;

  WebSocketChannel? _webSocketChannel;
  late String _currentUrl;
  late String _currentToken;

  Future<void> connect(String url, String token) async {
    try {
      _currentUrl = url;
      _currentToken = token;

      final connectUrl = token.isEmpty ? url : '$url?token=$token';
      final uri = Uri.parse(connectUrl);

      // _webSocketChannel = WebSocketChannel.connect(uri);

      HttpClient? httpClient;

      final bool enableProxy =
          GetStorage().read(StorageKey.ENABLE_PROXY) ?? false;
      logE('connect()-->enableProxy:$enableProxy');

      if (kDebugMode && enableProxy) {
        final String proxyAddress = GetStorage().read(StorageKey.PROXY_ADDRESS);
        logE('connect()-->proxyAddress:$proxyAddress');

        SecurityContext ctx = SecurityContext.defaultContext;
        httpClient =
            HttpClient(context: ctx)
              ..findProxy = ((uri) {
                // return 'PROXY localhost:8888';
                return 'PROXY $proxyAddress';
              })
              ..badCertificateCallback = (cert, host, port) {
                return true;
              }
              ..userAgent = 'im_socket';
      }

      _webSocketChannel = IOWebSocketChannel.connect(
        uri,
        pingInterval: Duration(seconds: 30),
        connectTimeout: Duration(seconds: 36000),
        // customClient:
        //     HttpClient()..badCertificateCallback = (cert, host, port) => true,
        customClient: httpClient,
      );

      await _webSocketChannel?.ready;
      isConnected.value = true;

      // _webSocketChannel!.stream.listen(
      //   (_) => isConnected.value = true,
      //   onDone: _onDisconnect,
      //   onError: (_) => _onDisconnect(),
      // );

      // _startHeartbeat();
    } catch (e) {
      _retryConnect();
    }
  }

  void sendMessage(dynamic data) {
    logE('sendMessage()-->data type:${data.runtimeType},data:$data');
    if (isConnected.value) {
      _webSocketChannel!.sink.add(data);
    }
  }

  Stream<dynamic> get stream {
    return _webSocketChannel!.stream;
  }

  void disconnect() {
    _onDisconnect();
  }

  void _onDisconnect() {
    if (_webSocketChannel != null) {
      isConnected.value = false;
      _webSocketChannel!.sink.close();
      // _retryConnect();
    }
  }

  void _retryConnect() {
    if (retryCount.value > 5) {
      return;
    }
    Future.delayed(Duration(seconds: 2 ^ retryCount.value), () {
      retryCount.value++;
      connect(_currentUrl, _currentToken);
    });
  }

  void _startHeartbeat() {
    Timer.periodic(Duration(seconds: 30), (timer) {
      if (!isConnected.value) timer.cancel();
      sendMessage({'type': 'heartbeat'});
    });
  }
}
